package com.jblv.frame.config;

import com.alibaba.fastjson.JSONObject;
import com.jblv.frame.config.event.ConfigChangeEvent;
import com.jblv.frame.config.event.ConfigChangeEventBus;
import com.jblv.frame.config.utils.ConfigBootstrapHttpUtils;
import com.jblv.frame.config.utils.ConfigBootstrapProperties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author happyonion.yangtao
 * @version 1.0
 * @title ConfigPropertiesSourceLoader
 * @projectName jblv-frame
 * @packageName com.jblv.frame.config
 * @description TODO
 * @date Create at 2020/9/7 17:33
 * @since jdk1.8
 */
public class ConfigPropertiesSourceLoader {

    private static Logger logger;
    private static ConfigPropertiesSourceLoader configPropertiesPropertySource;
    private Properties properties;

    public Properties getProperties() {
        return properties;
    }

    private ConfigPropertiesSourceLoader() {
        logger = LoggerFactory.getLogger(ConfigPropertiesSourceLoader.class);
        properties = loadProperties();
    }

    public static ConfigPropertiesSourceLoader build() {
        if (ConfigPropertiesSourceLoader.configPropertiesPropertySource != null) {
            return ConfigPropertiesSourceLoader.configPropertiesPropertySource;
        }
        synchronized (ConfigPropertiesSourceLoader.class) {
            if (ConfigPropertiesSourceLoader.configPropertiesPropertySource != null) {
                return ConfigPropertiesSourceLoader.configPropertiesPropertySource;
            }
            if (ConfigPropertiesSourceLoader.configPropertiesPropertySource == null) {
                ConfigPropertiesSourceLoader.configPropertiesPropertySource = new ConfigPropertiesSourceLoader();
            }
        }
        return ConfigPropertiesSourceLoader.configPropertiesPropertySource;
    }

    public void refreshPropertySources() throws Exception {
        logger.info("start refresh property sources");
        final Properties before = this.properties;
        final Properties after = this.loadProperties();
        if (after == null || after.size() == 0) {
            logger.error("refresh property sources , but load properties is empty.");
            return;
        }
        final Properties change = this.changeProperties(before, after);
        if (change == null || change.size() == 0) {
            logger.info("refresh property sources , but change config is empty.");
            return;
        }
        for (final Object key : (change).keySet()) {
            final String value = change.getProperty(key.toString());
            ConfigCenterProperties.getProperties().setProperty(key.toString().trim(), value);
            RefreshScopeBeanHolder.refresh(key.toString().trim(), value);
        }
        this.properties = after;
        final ConfigChangeEvent event = new ConfigChangeEvent();
        event.setChange(change);
        ConfigChangeEventBus.build().postEvent(event);
        logger.info("start refresh property sources success.");
    }

    private Properties loadProperties() {
        logger.info("start loading properties.....,");
        JSONObject json = ConfigBootstrapHttpUtils.getConfig();
        Properties properties = new Properties();
        for (String key : json.keySet()) {
            (properties).put(key, json.getJSONObject(key).get("value"));
        }
        (properties).put("jblv.config.uri", ConfigBootstrapProperties.getUri());
        for (ApplicationLabelEnum label : ApplicationLabelEnum.values()) {
            String id = label.getLabel() + ".id";
            String name = label.getLabel() + ".name";
            String host = label.getLabel() + ".host";
            if (properties.getProperty(host).contains(ConfigBootstrapProperties.getHost())) {
                (properties).put("jblv.application.id", (properties).get(id));
                (properties).put("jblv.application.type", label.getType());
                (properties).put("jblv.application.name", (properties).get(name));
                String ip = ConfigBootstrapProperties.getHost().split(":")[0];
                String port = ConfigBootstrapProperties.getHost().split(":")[1];
                String regex = properties.getProperty("jblv.application.ip.regex");
                ip = getIp(ip, regex);
                (properties).put("jblv.application.host", ip + ":" + port);
                logger.info("jblv.application.host = " + ip + ":" + port);
                (properties).put("jblv.application.ip", ip);
                (properties).put("jblv.application.port", port);
                break;
            }
        }
        logger.info("loaded properties successfully.");
        return properties;
    }

    private boolean equal(final Object one, final Object two) {
        return (one == null && two == null) || (one != null && two != null && one.equals(two));
    }

    private Properties changeProperties(final Properties before, final Properties after) {
        Properties result = new Properties();
        for (Object key : (before).keySet()) {
            if (!after.containsKey(key)) {
                (result).put(key, "");
            }
            else {
                if (this.equal((before).get(key), (after).get(key))) {
                    continue;
                }
                result.put(key, (after).get(key));
            }
        }
        for (Object key : (after).keySet()) {
            if (!before.containsKey(key)) {
                result.put(key, (after).get(key));
            }
        }
        return result;
    }

    private String getIp(String defaultIp, String regex) {
        try {
            Set<String> ips = new HashSet<>();
            Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
            while (en.hasMoreElements()) {
                NetworkInterface intf = en.nextElement();
                Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
                while (enumIpAddr.hasMoreElements()) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
                        String ip = inetAddress.getHostAddress();
                        if (ip.contains("::") || ip.contains("0:0:")) {
                            continue;
                        }
                        if ("127.0.0.1".equals(ip)) {
                            continue;
                        }
                        if (ip.equals(defaultIp)) {
                            return ip;
                        }
                        ips.add(ip);
                    }
                }
            }
            if (StringUtils.isNotBlank(regex)) {
                Pattern p = Pattern.compile(regex);
                for (String ip2 : ips) {
                    Matcher matcher = p.matcher(ip2);
                    if (matcher.matches()) {
                        return ip2;
                    }
                }
            }
            return defaultIp;
        }
        catch (Exception e) {
            logger.error("获取ip异常：", e);
            return defaultIp;
        }
    }

    
}
